﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Content;

using RpgLibrary.CharacterClasses;
using RpgLibrary.ItemClasses;
using RpgLibrary.WorldClasses;

using XRpgLibrary.TileEngine;
using XRpgLibrary.SpriteClasses;
using XRpgLibrary.ScriptClasses;
using XRpgLibrary.CharacterClasses;
using XRpgLibrary.FadeClasses;

namespace XRpgLibrary.WorldClasses
{
    public class World : DrawableGameComponent, IGameDrawable, IGameUpdateable
    {
        public Fade Fader;
        
        private bool isPaused = false;
        public bool IsPaused
        {
            get
            {
                return isPaused;
            }
        }

        public Camera Camera
        {
            get;
            set;
        }

        #region Graphic Field and Property Region

        protected SpriteBatch spriteBatch;

        Rectangle screenRect;
        public Rectangle ScreenRectangle
        {
            get { return screenRect; }
        }
        #endregion
        
        #region Level Field and Property Region

        readonly List<Level> levels = new List<Level>();
        int currentLevelIndex = -1;
        
        public List<Level> Levels
        {
            get { return levels; }
        }
        
        public int CurrentLevelIndex
        {
            get { return currentLevelIndex; }
            set
            {
                if (value < 0 || value >= levels.Count)
                    throw new IndexOutOfRangeException();
                
                if (levels[value] == null)
                    throw new NullReferenceException();
                
                currentLevelIndex = value;
            }
        }

        public Level CurrentLevel
        {
            get
            {
                return levels[CurrentLevelIndex];
            }
        }

        #endregion
        
        #region Constructor Region

        public World(RpgGame game, Rectangle screenRectangle, SpriteBatch spriteBatch, ScriptContext scriptContext)
            : base(game)
        {
            this.screenRect = screenRectangle;
            this.spriteBatch = spriteBatch;

            LoadLevels(game, spriteBatch, scriptContext);
        }
        
        #endregion
        
        #region Method Region

        public void Pause()
        {
            isPaused = true;
        }

        public void Unpause()
        {
            isPaused = false;
        }

        public void TogglePause()
        {
            isPaused = !isPaused;
        }

        private void LoadLevels(Game game, SpriteBatch spriteBatch, ScriptContext scriptContext)
        {
            string levelDirectory = @"Game\Levels";
            string mapDirectory = Path.Combine(levelDirectory, "Maps");

            string[] levelNames = Directory.GetFiles(
                Path.Combine(game.Content.RootDirectory, levelDirectory), 
                "*.xnb");

            foreach(string levelName in levelNames)
            {
                LevelData levelData = game.Content.Load<LevelData>(
                    Path.Combine(levelDirectory, Path.GetFileNameWithoutExtension(levelName)));
                Level level = Level.LoadFromData(levelData, spriteBatch);

                MapData mapData = game.Content.Load<MapData>(
                    Path.Combine(mapDirectory, level.MapName));
                level.Map = new TileMap(mapData.LoadTilesets(game), mapData.LoadLayers(game, spriteBatch, scriptContext, Camera));

                Levels.Add(level);
            }
        }

        public override void Update(GameTime gameTime)
        {
            levels[currentLevelIndex].Update(gameTime);

            if (Fader != null)
            {
                if (Fader.IsAlive)
                    Fader.Update(gameTime);
                else
                    Fader = null;
            }
        }

        public override void Draw(GameTime gameTime)
        {
            Draw(gameTime, Camera);
            base.Draw(gameTime);
        }

        public void Draw(GameTime gameTime, Camera camera)
        {
            levels[currentLevelIndex].Draw(gameTime, camera);
            if (Fader != null)
                Fader.Draw(gameTime, camera);
        }

        #endregion

        internal void MoveToLevel(Character character, string levelName, string layerName)
        {
            Level level = GetLevelByName(levelName);

            if (level == null)
                return;

            MapLayer layer = level.GetLayerByName(layerName);

            if (layer == null)
                return;

            layer.CharacterManager.Characters.Add(character);

            CurrentLevel.RemoveCharacter(character);
        }

        private Level GetLevelByName(string levelName)
        {
            for (int i = 0, cnt = Levels.Count; i < cnt; i++)
                if (Levels[i].MapName == levelName)
                    return Levels[i];

            return null;
        }
    }
}